﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.ComponentModel.Composition;

using NuMetaheuristics;
using NuMetaheuristics.Genotypes;
using NuMetaheuristics.Utility;

namespace NuMetaheuristics.Operators.Vector
{
    /// <summary>
    /// Produce a vector from two given vectors by combining elements from 
    /// each vector in a uniformly random fashion.
    /// </summary>
    [Export(typeof(IBinaryOperator<DoubleVectorGenotype>))]
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    public class DoubleVectorUniformCrossover : IBinaryOperator<DoubleVectorGenotype>, IDeserializationCallback
    {
        private RandomNumberGenerator _rng;

        public DoubleVectorUniformCrossover()
        {
            _rng = new RandomNumberGenerator();
        }

        public void OnDeserialization(object sender)
        {
            _rng = new RandomNumberGenerator();
        }
        
        public DoubleVectorGenotype Operate(DoubleVectorGenotype operandL, DoubleVectorGenotype operandR)
        {
            if (_rng == null)
                throw new NullReferenceException();

            if (operandL == null || operandR == null)
                throw new ArgumentNullException();

            DoubleVectorGenotype longer = null;
            DoubleVectorGenotype shorter = null;
            if (operandL.Vector.Length > operandR.Vector.Length)
            {
                longer = operandL;
                shorter = operandR;
            }
            else
            {
                longer = operandR;
                shorter = operandL;
            }

            List<int> crossoverPoints = null;
            {
                int nPoints = _rng.Next(1, longer.Vector.Length + 1);
                List<int> points = new List<int>();

                for (int i = 0; i < nPoints; i++)
                {
                    int point = _rng.Next(1, longer.Vector.Length);
                    points.Add(point);
                }
                crossoverPoints = points.Distinct().ToList<int>();
                crossoverPoints.Sort();
            }

            List<double> resultant = new List<double>();
            int lastPoint = 0;
            DoubleVectorGenotype source = shorter;

            //for(int i = 0; i < longer.Length; i++)
            for (int i = 0; i < crossoverPoints.Count; i++)
            {
                if (i % 2 == 0)
                {
                    source = shorter;
                }
                else
                {
                    source = longer;
                }

                int point = crossoverPoints[i];

                for (int j = lastPoint; j < point && j < source.Vector.Length; j++)
                {
                    resultant.Add(source.Vector[j]);
                }

                lastPoint = point;
            }

            for (int j = lastPoint; j < source.Vector.Length; j++)
            {
                resultant.Add(source.Vector[j]);
            }

            return new DoubleVectorGenotype(resultant.ToArray());
        }
    }

    /// <summary>
    /// Produce a vector from two given vectors by combining elements from 
    /// each vector in a uniformly random fashion.
    /// </summary>
    [Export(typeof(IBinaryOperator<IntegerVectorGenotype>))]
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    public class IntegerVectorUniformCrossover : IBinaryOperator<IntegerVectorGenotype>, IDeserializationCallback
    {
        private RandomNumberGenerator _rng;

        public IntegerVectorUniformCrossover()
        {
            _rng = new RandomNumberGenerator();
        }

        public void OnDeserialization(object sender)
        {
            _rng = new RandomNumberGenerator();
        }
        
        public IntegerVectorGenotype Operate(IntegerVectorGenotype operandL, IntegerVectorGenotype operandR)
        {
            if (_rng == null)
                throw new NullReferenceException();

            if (operandL == null || operandR == null)
                throw new ArgumentNullException();

            IntegerVectorGenotype longer = null;
            IntegerVectorGenotype shorter = null;
            if (operandL.Vector.Length > operandR.Vector.Length)
            {
                longer = operandL;
                shorter = operandR;
            }
            else
            {
                longer = operandR;
                shorter = operandL;
            }

            List<int> crossoverPoints = null;
            {
                int nPoints = _rng.Next(1, longer.Vector.Length + 1);
                List<int> points = new List<int>();

                for (int i = 0; i < nPoints; i++)
                {
                    int point = _rng.Next(1, longer.Vector.Length);
                    points.Add(point);
                }
                crossoverPoints = points.Distinct().ToList<int>();
                crossoverPoints.Sort();
            }

            List<int> resultant = new List<int>();
            int lastPoint = 0;
            IntegerVectorGenotype source = shorter;

            //for(int i = 0; i < longer.Length; i++)
            for (int i = 0; i < crossoverPoints.Count; i++)
            {
                if (i % 2 == 0)
                {
                    source = shorter;
                }
                else
                {
                    source = longer;
                }

                int point = crossoverPoints[i];

                for (int j = lastPoint; j < point && j < source.Vector.Length; j++)
                {
                    resultant.Add(source.Vector[j]);
                }

                lastPoint = point;
            }

            for (int j = lastPoint; j < source.Vector.Length; j++)
            {
                resultant.Add(source.Vector[j]);
            }

            return new IntegerVectorGenotype(resultant.ToArray());
        }
    }

    /// <summary>
    /// Produce a vector from two given vectors by combining elements from 
    /// each vector in a uniformly random fashion.
    /// </summary>
    [Export(typeof(IBinaryOperator<BooleanVectorGenotype>))]
    [DataContract(Namespace = "http://numetaheuristics.codeplex.com/schemas")]
    public class BooleanVectorUniformCrossover : IBinaryOperator<BooleanVectorGenotype>, IDeserializationCallback
    {
        private RandomNumberGenerator _rng;

        public BooleanVectorUniformCrossover()
        {
            _rng = new RandomNumberGenerator();
        }

        public void OnDeserialization(object sender)
        {
            _rng = new RandomNumberGenerator();
        }

        public BooleanVectorGenotype Operate(BooleanVectorGenotype operandL, BooleanVectorGenotype operandR)
        {
            if (_rng == null)
                throw new NullReferenceException();

            if (operandL == null || operandR == null)
                throw new ArgumentNullException();

            BooleanVectorGenotype longer = null;
            BooleanVectorGenotype shorter = null;
            if (operandL.Vector.Length > operandR.Vector.Length)
            {
                longer = operandL;
                shorter = operandR;
            }
            else
            {
                longer = operandR;
                shorter = operandL;
            }

            List<int> crossoverPoints = null;
            {
                int nPoints = _rng.Next(1, longer.Vector.Length + 1);
                List<int> points = new List<int>();

                for (int i = 0; i < nPoints; i++)
                {
                    int point = _rng.Next(1, longer.Vector.Length);
                    points.Add(point);
                }
                crossoverPoints = points.Distinct().ToList<int>();
                crossoverPoints.Sort();
            }

            List<bool> resultant = new List<bool>();
            int lastPoint = 0;
            BooleanVectorGenotype source = shorter;

            //for(int i = 0; i < longer.Length; i++)
            for (int i = 0; i < crossoverPoints.Count; i++)
            {
                if (i % 2 == 0)
                {
                    source = shorter;
                }
                else
                {
                    source = longer;
                }

                int point = crossoverPoints[i];

                for (int j = lastPoint; j < point && j < source.Vector.Length; j++)
                {
                    resultant.Add(source.Vector[j]);
                }

                lastPoint = point;
            }

            for (int j = lastPoint; j < source.Vector.Length; j++)
            {
                resultant.Add(source.Vector[j]);
            }

            return new BooleanVectorGenotype(resultant.ToArray());
        }
    }
}
